﻿namespace HEFramework
{
    public static class TimerComponentSystem
    {
        [EntitySystem]
        public class TimerComponentAwakeSystem : AwakeSystem<TimerComponent>
        {
            protected override void Awake(TimerComponent _self)
            {
            }
        }


        [EntitySystem]
        public class TimerComponentUpdateSystem : UpdateSystem<TimerComponent>
        {
            protected override void Update(TimerComponent _self)
            {
                if (_self.timeId.Count == 0)
                {
                    return;
                }

                long timeNow = _self.GetNow();

                //表示时间轴未来到最近一个计时器的节点
                if (timeNow < _self.MinTime)
                {
                    return;
                }

                using (var enumerator = _self.timeId.GetEnumerator())
                {
                    while (enumerator.MoveNext())
                    {
                        var kv = enumerator.Current;
                        long k = kv.Key;
                        if (k > timeNow)
                        {
                            _self.MinTime = k;
                            break;
                        }

                        _self.timeOutTime.Enqueue(k);
                    }
                }

                while (_self.timeOutTime.Count > 0)
                {
                    long time = _self.timeOutTime.Dequeue();
                    var list = _self.timeId[time];
                    for (int i = 0; i < list.Count; ++i)
                    {
                        long timerId = list[i];
                        _self.timeOutTimerIds.Enqueue(timerId);
                    }

                    _self.timeId.Remove(time);
                }

                if (_self.timeId.Count == 0)
                {
                    _self.MinTime = long.MaxValue;
                }

                while (_self.timeOutTimerIds.Count > 0)
                {
                    long timerId = _self.timeOutTimerIds.Dequeue();

                    if (!_self.timerActions.Remove(timerId, out TimerAction timerAction))
                    {
                        continue;
                    }

                    _self.Run(timerAction);
                }
            }
        }


        /// <summary>
        /// 未来某个时间点执行
        /// </summary>
        /// <param name="_self"></param>
        /// <param name="_tillTime"></param>
        /// <param name="_cancellationToken"></param>
        public static async HETask WaitTillAsync(this TimerComponent _self, long _tillTime, HECancellationToken _cancellationToken = null)
        {
            long timeNow = _self.GetNow();
            if (timeNow >= _tillTime)
            {
                return;
            }

            HETask tcs = HETask.Create(true);
            TimerAction timer = TimerAction.Create(_self.GetId(), eTimerClass.OnceWaitTimer, timeNow, _tillTime - timeNow, 0, tcs);
            _self.AddTimer(timer);
            long timerId = timer.Id;

            void CancelAction()
            {
                if (_self.Remove(timerId))
                {
                    tcs.SetResult();
                }
            }

            try
            {
                _cancellationToken?.Add(CancelAction);
                await tcs;
            }
            finally
            {
                _cancellationToken?.Remove(CancelAction);
            }
        }

        /// <summary>
        /// 一帧后执行
        /// </summary>
        /// <param name="_self"></param>
        /// <param name="_cancellationToken"></param>
        public static async HETask WaitFrameAsync(this TimerComponent _self, HECancellationToken _cancellationToken = null)
        {
            await _self.WaitAsync(1, _cancellationToken);
        }

        /// <summary>
        /// 指定时间后执行
        /// </summary>
        /// <param name="_self"></param>
        /// <param name="_time"></param>
        /// <param name="_cancellationToken"></param>
        public static async HETask WaitAsync(this TimerComponent _self, long _time, HECancellationToken _cancellationToken = null)
        {
            if (_time == 0)
            {
                return;
            }

            long timeNow = _self.GetNow();

            HETask tcs = HETask.Create(true);
            TimerAction timer = TimerAction.Create(_self.GetId(), eTimerClass.OnceWaitTimer, timeNow, _time, 0, tcs);
            _self.AddTimer(timer);
            long timerId = timer.Id;

            void CancelAction()
            {
                if (_self.Remove(timerId))
                {
                    tcs.SetResult();
                }
            }

            try
            {
                _cancellationToken?.Add(CancelAction);
                await tcs;
            }
            finally
            {
                _cancellationToken?.Remove(CancelAction);
            }
        }

        /// <summary>
        /// 重复执行
        /// </summary>
        /// <param name="_self"></param>
        /// <param name="_time"></param>
        /// <param name="_type"></param>
        /// <param name="_args"></param>
        /// <returns></returns>
        public static long NewRepeatedTimer(this TimerComponent _self, long _time, int _type, object _args)
        {
            if (_time < 100)
            {
                Log.Error($"Time too small: {_time}");
                return 0;
            }

            return _self.NewRepeatedTimerInner(_time, _type, _args);
        }

        // 用这个优点是可以热更，缺点是回调式的写法，逻辑不连贯。WaitTillAsync不能热更，优点是逻辑连贯。
        // wait时间短并且逻辑需要连贯的建议WaitTillAsync
        // wait时间长不需要逻辑连贯的建议用NewOnceTimer
        public static long NewOnceTimer(this TimerComponent _self, long _tillTime, int _type, object _args)
        {
            long timeNow = _self.GetNow();
            if (_tillTime < timeNow)
            {
                Log.Error($"New once time too small: {_tillTime}");
            }

            TimerAction timer = TimerAction.Create(_self.GetId(), eTimerClass.OnceTimer, timeNow, _tillTime - timeNow, _type, _args);
            _self.AddTimer(timer);
            return timer.Id;
        }

        public static long NewFrameTimer(this TimerComponent _self, int _type, object _args)
        {
#if DOTNET
            return self.NewRepeatedTimerInner(100, _type, _args);
#else
            return _self.NewRepeatedTimerInner(0, _type, _args);
#endif
        }

        public static bool Remove(this TimerComponent _self, ref long _id)
        {
            long i = _id;
            _id = 0;
            return _self.Remove(i);
        }


        private static long GetId(this TimerComponent _self)
        {
            return ++_self.IdGenerator;
        }

        private static long GetNow(this TimerComponent _self)
        {
            return TimeRunner.Instance.ClientFrameTime();
        }

        private static void Run(this TimerComponent _self, TimerAction _timerAction)
        {
            switch (_timerAction.TimerClass)
            {
                case eTimerClass.OnceTimer:
                {
                    EventListener.Instance.Invoke(_timerAction.Type, new TimerCallback() { args = _timerAction.Object });
                    _timerAction.Recycle();
                    break;
                }
                case eTimerClass.OnceWaitTimer:
                {
                    HETask tcs = _timerAction.Object as HETask;
                    tcs.SetResult();
                    _timerAction.Recycle();
                    break;
                }
                case eTimerClass.RepeatedTimer:
                {
                    long timeNow = _self.GetNow();
                    _timerAction.StartTime = timeNow;
                    _self.AddTimer(_timerAction);
                    EventListener.Instance.Invoke(_timerAction.Type, new TimerCallback() { args = _timerAction.Object });
                    break;
                }
            }
        }

        private static void AddTimer(this TimerComponent _self, TimerAction _timer)
        {
            long tillTime = _timer.StartTime + _timer.Time;
            _self.timeId.Add(tillTime, _timer.Id);
            _self.timerActions.Add(_timer.Id, _timer);
            if (tillTime < _self.MinTime)
            {
                _self.MinTime = tillTime;
            }
        }

        private static bool Remove(this TimerComponent _self, long _id)
        {
            if (_id == 0)
            {
                return false;
            }

            if (!_self.timerActions.Remove(_id, out TimerAction timerAction))
            {
                return false;
            }

            timerAction.Recycle();
            return true;
        }

        private static long NewRepeatedTimerInner(this TimerComponent _self, long _time, int _type, object _args)
        {
#if DOTNET
            if (_time < 100)
            {
                throw new Exception($"Repeated timer < 100, timerType: time: {_time}");
            }
#endif

            long timeNow = _self.GetNow();
            TimerAction timer = TimerAction.Create(_self.GetId(), eTimerClass.RepeatedTimer, timeNow, _time, _type, _args);

            // 每帧执行的不用加到timerId中，防止遍历
            _self.AddTimer(timer);
            return timer.Id;
        }
    }
}